'''HelpWindow

Display simple HTML in a popup window.

This supports only simple HTML. An index is build out of all <h1> and
<h2> elements.

'''

import wx
import wx.html
from HTMLParser import HTMLParser
import re

class HelpWindow(wx.Frame):
    # this should be based on a number of characters
    # in the default font...
    index_width = 250

    def __init__(self, parent, title="Help", size=(800,400), show_index = True):
        super(HelpWindow, self).__init__(parent, wx.ID_ANY, title, size=size)
        self.show_index = show_index
        self.splitter = wx.SplitterWindow(self, wx.ID_ANY)
        self.index = wx.TreeCtrl(self.splitter, wx.ID_ANY, style=wx.TR_HIDE_ROOT)
        self.html = wx.html.HtmlWindow(self.splitter, wx.ID_ANY)
        self.html.SetRelatedFrame(self, title)
        self.html.SetRelatedStatusBar(0)
        self.splitter.SplitVertically(self.index, self.html, self.index_width)
        self.ShowIndex(show_index)
        self.CenterOnScreen()
        self.index.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnIndexSelectionChanged)
        # default H1 sizes are too big IMO; this tones
        # them down a bit
        self.html.SetStandardFonts()
        font = self.GetFont()
        points = font.GetPointSize()
        self.html.SetFonts("", "", [points-2, points-1, points, points+1, points+1, points+1, points+2])

    def OnIndexSelectionChanged(self, event):
        '''Called when user clicks on an item in the index'''
        item = event.GetItem()
        if item == self.index.GetRootItem(): return
        anchor_number = self.index.GetItemPyData(item)
        self.ScrollToAnchor(str(anchor_number))

    def ScrollToAnchor(self, anchor):
        '''Scroll to the given anchor'''
        self.html.ScrollToAnchor(anchor)

    def SetPage(self, html_string):
        '''Sets the HTML contents for the page'''
        # this creates an index by adding anchors around
        # every h1 and h2 element 
        html = self._create_index(html_string)
        self.html.SetPage(html)
        self.index.ExpandAll()

    def ShowIndex(self, show_index=True):
        '''Turn display of the index on or off'''
        self.show_index = show_index
        if show_index:
            self.splitter.SplitVertically(self.index, self.html, 
                                          self.index_width)
        else:
            self.splitter.Unsplit(self.index)
            self.index_width = self.splitter.GetSashPosition()

    def _create_index(self, page_source):
        '''Adds anchors to every h1 and h2, and builds an index'''
        self.index.DeleteAllItems()
        root = self.index.AddRoot("Table of contents")
        self._parent_nodes = [root]
        # this call has a side effect of adding an item to the index
        # tree in addition to adding markup to the html
        html=re.sub(r'<[hH]([12])>(.*?)</[hH]\1>', 
                    self._add_anchor_and_tree_item, page_source)
        return html

    def _add_anchor_and_tree_item(self, match):
        '''Add an anchor to the matched text, and create a tree item'''
        level = match.group(1)
        text = match.group(2)
        anchor_number = self.index.GetCount()
        if level == "1":
            item = self.index.AppendItem(self.index.GetRootItem(), text)
            self._parent_nodes.append(item)
            self.index.SetItemPyData(item,anchor_number)
        else:
            parent = self._parent_nodes[-1]
            item = self.index.AppendItem(parent, text)
            self.index.SetItemPyData(item,anchor_number)
        # we give it both a numerically unique anchor name, and also a
        # name that is the same as the text. This second anchor makes
        # it easy (albeit error prone) to manually add links when
        # editing the HTML.
        return "<a name='%s'><a name='%s'><h%s>%s</h%s></a></a>" % (text, anchor_number, level, text, level)

